/*
 *  This file is part of the Jikes RVM project (http://jikesrvm.org).
 *
 *  This file is licensed to You under the Eclipse Public License (EPL);
 *  You may not use this file except in compliance with the License. You
 *  may obtain a copy of the License at
 *
 *      http://www.opensource.org/licenses/eclipse-1.0.php
 *
 *  See the COPYRIGHT.txt file distributed with this work for information
 *  regarding copyright ownership.
 */
package org.jikesrvm.mm.mminterface;

import org.jikesrvm.VM;
import org.jikesrvm.classloader.RVMArray;
import org.jikesrvm.classloader.RVMClass;
import org.jikesrvm.classloader.RVMType;
import org.jikesrvm.objectmodel.JavaHeader;
import org.jikesrvm.objectmodel.ObjectModel;
import org.jikesrvm.objectmodel.TIB;
import org.jikesrvm.runtime.BootRecord;
import org.jikesrvm.runtime.Magic;
import org.jikesrvm.scheduler.RVMThread;
import org.mmtk.policy.Space;
import org.mmtk.utility.Log;
import org.mmtk.utility.heap.Mmapper;
import org.mmtk.utility.options.Options;
import org.vmmagic.pragma.Interruptible;
import org.vmmagic.pragma.Uninterruptible;
import org.vmmagic.unboxed.Address;
import org.vmmagic.unboxed.ObjectReference;

/**
 * Common debugging utility functions used by various garbage collectors
 */
@Uninterruptible
public class DebugUtil implements org.mmtk.utility.Constants, org.jikesrvm.Constants {

  private static TIB tibForArrayType;
  private static TIB tibForClassType;
  private static TIB tibForPrimitiveType;

  @Interruptible
  static void boot(BootRecord theBootRecord) {
    // get addresses of TIBs for RVMArray & RVMClass used for testing Type ptrs
    RVMType t = RVMArray.IntArray;
    tibForArrayType = ObjectModel.getTIB(t);
    tibForPrimitiveType = ObjectModel.getTIB(RVMType.IntType);
    t = Magic.getObjectType(BootRecord.the_boot_record);
    tibForClassType = ObjectModel.getTIB(t);
  }

  /**
   * Check if an address appears to point to an instance of RVMType
   *
   * @param typeAddress the address to check
   */
  private static boolean DEBUG = true;
  @Uninterruptible
  public static boolean validType(ObjectReference typeAddress) {
    if (!Space.isMappedObject(typeAddress)) {
      return false;  // type address is outside of heap
    }

    // check if types tib is one of three possible values
	if (typeAddress == null) {
		if (DEBUG) {
			Log.writeln("DebugUtil.validType called for null");
			Log.flush();
		} 
		return false;
	}else {
		Object o = typeAddress.toObject();
		ObjectReference or = ObjectReference.fromObject(o);
		long longO = or.toAddress().toLong();
		if(longO%4 != 0) {
			if (DEBUG) {
				Log.write("DebugUtil.validType called for ");
				Log.write(ObjectReference.fromObject(typeAddress));
				Log.write(", or=");
				Log.writeln(or);
				Log.flush();
			}
			return false;
		}		
	}
	
	/*
	Address addr = typeAddress.toAddress();
	if(addr != ObjectReference.fromObject(tibForClassType).toAddress() &&
			addr != ObjectReference.fromObject(tibForArrayType).toAddress() &&
			addr != ObjectReference.fromObject(tibForPrimitiveType).toAddress()) {
		if (DEBUG) {
			Log.write("DebugUtil.validType compare as address failed for ");
			Log.write(typeAddress);
			Log.write(", addr=");
			Log.writeln(addr);
			Log.flush();
		}
		return false;
	}*/
	
    TIB typeTib = ObjectModel.getTIB(typeAddress);
    return ((typeTib == tibForClassType) || (typeTib == tibForArrayType) || (typeTib == tibForPrimitiveType));
  }

  /**
   * Dump all threads & their stacks starting at the frame identified
   * by the threads saved contextRegisters (ip & fp fields).
   */
  @Uninterruptible
  public static void dumpAllThreadStacks() {
    RVMThread.dumpVirtualMachine();
  }  // dumpAllThreadStacks

  /**
   * Check if a ref, its tib pointer & type pointer are all in the heap
   */
  @Uninterruptible
  public static boolean validObject(Object ref) {
    return validRef(ObjectReference.fromObject(ref));
  }

  private static boolean basicRefTest(ObjectReference ref, boolean verbose) {
		if (ref.isNull()) {
			//if (verbose) {
			//	VM.sysWrite("basicRefTest, ref is null");
			//}
			return false;
		}

		if (!mappedVMRef(ref)) {
			//if (verbose) {
			//	VM.sysWrite("basicRefTest, ref is outside the heap or not mapped ");
			//	VM.sysWriteln(ref);
			//}
			return false;
		}

		long longO = ref.toAddress().toLong();
		if (longO % 4 != 0) {
			if (verbose) {
				VM.sysWrite("basicRefTest, ref is not alligned ");
				VM.sysWriteln(ref);
			}
			return false;
		}

		return true;
	}
  
  	private static boolean spaceRefTest(ObjectReference ref, boolean verbose) {
  		Space space = Space.getSpaceForObject(ref);
  		if(space == null) {
  			if (verbose) {
				VM.sysWrite("spaceRefTest, space is null ");
				VM.sysWriteln(ref);
			}
  			return false;
  		}
		if(!space.validRef(ref)) {
			if (verbose) {
				VM.sysWrite("spaceRefTest, ref is not valid in its space ");
				VM.sysWrite(ref);
				VM.sysWrite(", space: ");
				VM.sysWriteln(space.getName());
			}
			return false;
		}
		
		return true;
		
  	}
  
  	@Uninterruptible
	public static boolean myValidRef(ObjectReference ref) {

		if (ref.isNull())
			return true;

		//alligned and in heap
		if (!basicRefTest(ref, true)) {
			if (Options.verbose.getValue() >= 9) {
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		TIB tib = ObjectModel.getTIB(ref);
		if (tib == null) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tib is null, ref=");
				VM.sysWrite(ref);
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		// kathy - I have moved it up the file, not happening
		Address tibAddr = Magic.objectAsAddress(tib);
		if (tibAddr.isZero()) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tib is zero as address, ref=");
				VM.sysWriteln(ref);
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		// kathy
		ObjectReference tibOR = ObjectReference.fromObject(tib);
		if (tibOR.isNull()) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWriteln("invalidRef: tibOR is null, ref=");
				VM.sysWriteln(ref);
				ObjectModel.dumpHeader(ref); // can do this here?
			}
			return false;
		}

		if (!basicRefTest(tibOR, true)) {
			if (Options.verbose.getValue() >= 9) {
				ObjectModel.dumpHeader(ref); // can do this here?
			}
			return false;
		}

		// kathy
		Space tibSpace = Space.getSpaceForObject(tibOR);
		if (tibSpace == null) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibOR has no space ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibOR=");
				VM.sysWrite(tibOR);
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}
		if (!tibSpace.isImmortal()) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibOR is not in immortal space ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibSpace=");
				VM.sysWrite(tibSpace.getName());
				VM.sysWrite(", tibOR=");
				VM.sysWrite(tibOR);
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		// kathy
		Space space = Space.getSpaceForObject(ref);
		if (space == null) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: no space, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		if (!space.validRef(ref)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: invalid in its space, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", space=");
				VM.sysWrite(space.getName());
				VM.sysWrite(", tibSpace=");
				VM.sysWrite(tibSpace.getName());
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}
		
		if (Options.verbose.getValue() >= 9) {
			VM.sysWrite("may be valid ref, valid in its space, ref=");
			VM.sysWrite(ref);
			VM.sysWrite(", space=");
			VM.sysWrite(space.getName());
			VM.sysWrite(", tibSpace=");
			VM.sysWrite(tibSpace.getName());
			VM.sysWrite(", header=");
			ObjectModel.dumpHeader(ref);
			VM.sysWrite("\n");
		}
		
		if(!Space.isInSpace(tibSpace.getDescriptor(), tibOR)) {
			if (Options.verbose.getValue() >= 3) {
				VM.sysWrite("invalidRef: tib is not in its space, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibSpace=");
				VM.sysWrite(tibSpace.getName());
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		if (!tibSpace.validRef(tibOR)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tib is invalid in its space, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibSpace=");
				VM.sysWrite(tibSpace.getName());
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}
		if (Options.verbose.getValue() >= 9) {
			VM.sysWrite("may be valid ref, tib valid in its space, ref=");
			VM.sysWrite(ref);
			VM.sysWrite(", tibSpace=");
			VM.sysWrite(tibSpace.getName());
			VM.sysWrite(", tib header=");
			ObjectModel.dumpHeader(tibOR);
			VM.sysWrite("\n");
		}
		/*
		if (tib.length() == 0) {
			if (Options.verbose.getValue() >= 3) {
				VM.sysWrite("validRef: TIB length zero, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}*/

		// kathy
		TIB tibTibOR = ObjectModel.getTIB(tibOR);
		if (tibTibOR == null) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibTibOR is null");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		// kathy
		ObjectReference tibORTibOR = ObjectReference.fromObject(tibTibOR);
		if (tibORTibOR.isNull()) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibORTibOR is null");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		if (!Space.isMappedObject(tibORTibOR)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibORTibOR is not mapped");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		if (!Space.isImmortal(tibORTibOR)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef: tibORTibOR not immortal as object ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibOR=");
				VM.sysWrite(tibOR);
				VM.sysWrite(", header=");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		RVMType rvmType = Magic.getObjectType(ref.toObject());
		ObjectReference itype = ObjectReference.fromObject(rvmType);
		// VM.sysWrite(" TYPE=");
		// VM.sysWrite(itype);
		if (!validType(itype)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef:  (INVALID TYPE: CLASS NOT ACCESSIBLE)\n");
				ObjectModel.dumpHeader(ref);
				VM.sysWrite("\n");
			}
			return false;
		}

		ObjectReference type = ObjectReference.fromObject(tib.getType());
		if (!validType(type)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("invalidRef:  invalid TYPE, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(Magic.objectAsAddress(tib));
				VM.sysWrite(" type = ");
				VM.sysWrite(type);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		if (Options.verbose.getValue() >= 9) {
			VM.sysWrite("validRef: ");
			dumpRef(ref);
		}
		return true;
	} // validRef

  	public static boolean isRecursive(ObjectReference objectRef, int spaceDescr) {
		boolean result = false;

		RVMType type = ObjectModel.getObjectType(objectRef.toObject());
		if (type.isClassType()) {
			RVMClass klass = type.asClass();	
			int[] offsets = klass.getReferenceOffsets();
			Address childAddress;
			ObjectReference child;
			RVMType childType;
			for (int i = 0; i < offsets.length; i++) {
				childAddress = objectRef.toAddress().plus(offsets[i]);
				child = childAddress.loadObjectReference();
				if (child == null) {
					continue;
				}
				
				if(validChildRef(child, spaceDescr) && validRef(child)) {
					childType = ObjectModel.getObjectType(child.toObject());
					if (childType == type) {
						result = true;
						break;
					}
				}
			}
		}

		return result;
	}
  
  	@Uninterruptible
	private static boolean validChildRef(ObjectReference ref, int spaceDescr) {
		if (!basicRefTest(ref, true)) {
			return false;
		}
		
		TIB tib = ObjectModel.getTIB(ref);
		ObjectReference tibOR = ObjectReference.fromObject(tib);
		if (!basicRefTest(tibOR, true)) {
			return false;
		}
		Address tibAddr = Magic.objectAsAddress(tib);
		if(tibOR != tibAddr.toObjectReference()) {
			return false;
		}
		
		Address refStatus = Magic.getWordAtOffset(ref, JavaHeader.STATUS_OFFSET).toAddress();
		if (mappedVMRef(refStatus.toObjectReference())) {
			return false;
		}
		
		Space space = Space.getSpaceForObject(ref);
  		if(space == null) {
  			return false;
  		}
  		if(space.getDescriptor() != spaceDescr ) {
  			return false;
  		}
		if(!space.validRef(ref)) {
			return false;
		}
		
		return true;
  	}
  	
  	@Uninterruptible
	public static void setDebug(boolean value) {
  		DEBUG = value;
  	}
  	
  	@Uninterruptible
	public static boolean validRef1(ObjectReference ref) {
  		if (ref.isNull()) {
			return true;
  		}
  		
  		if(DEBUG) {
  			return verboseValidRef(ref, true);
  		}
  		return verboseValidRef(ref, false);
  	}
  	
  	@Uninterruptible
	private static boolean verboseValidRef(ObjectReference ref, boolean verbose) {
		if (ref.isNull())
			return true;

		if (!basicRefTest(ref, verbose)) {
			return false;
		}
		
		TIB tib = ObjectModel.getTIB(ref);
		ObjectReference tibOR = ObjectReference.fromObject(tib);
		if (!basicRefTest(tibOR, verbose)) {
			return false;
		}
		Address tibAddr = Magic.objectAsAddress(tib);
		if(tibOR != tibAddr.toObjectReference()) {
			if (verbose) {
				VM.sysWrite("validRef: suspicious tib, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", tibOR=");
				VM.sysWrite(tibOR);
				VM.sysWrite(", tibAddr=");
				VM.sysWriteln(tibAddr); 
			}
			return false;
		}
		
		Address refStatus = Magic.getWordAtOffset(ref, JavaHeader.STATUS_OFFSET).toAddress();
		if (mappedVMRef(refStatus.toObjectReference())) {
			if (verbose) {
				VM.sysWrite("validRef: status looks like heap object, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", status=");
				VM.sysWriteln(refStatus); 
			}
			return false;
		}
		
		if (!spaceRefTest(tibOR, verbose)) {
			return false;
		}
		
		if (!spaceRefTest(ref, verbose)) {
			return false;
		}
		
		if(!Space.isMappedObject(ObjectReference.fromObject(ObjectModel.getTIB(tibOR)))) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TIB gettib is not mapped, ref=");
				VM.sysWriteln(ref);
			}
			return false;
		}

		if (tib == null || tib.length() == 0) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TIB length zero, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		RVMType rvmType = Magic.getObjectType(ref.toObject());
		ObjectReference itype = ObjectReference.fromObject(rvmType);
		if (!mappedVMRef(itype)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: type OUTSIDE OF HEAP OR NOT MAPPED, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", itype=");
				VM.sysWriteln(itype);
			}
			return false;
		}
		
		if (!validType(itype)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TYPE: CLASS NOT ACCESSIBLE, ref=");
				VM.sysWriteln(ref);
			}
			return false;
		}

		ObjectReference type = ObjectReference.fromObject(tib.getType());
		if (!validType(type)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: invalid TYPE, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(Magic.objectAsAddress(tib));
				VM.sysWrite(" type = ");
				VM.sysWrite(type);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		return true;
	} // validRef
  
  @Uninterruptible
  public static boolean mappedVMRef(ObjectReference ref) {
    return Space.isMappedObject(ref) && Mmapper.objectIsMapped(ref);
  }
  
  // Kathy - this method is modified to be able to dump my custom objects
  @Uninterruptible
  public static void dumpRef(ObjectReference ref) {
    VM.sysWrite("REF=");
    if (ref.isNull()) {
      VM.sysWrite("NULL\n");
      return;
    }
    VM.sysWrite(ref);
    if (!mappedVMRef(ref)) {
      VM.sysWrite(" (REF OUTSIDE OF HEAP OR NOT MAPPED)\n");
      return;
    }
    Address tibAddr = Magic.objectAsAddress(ObjectModel.getTIB(ref));
    if(tibAddr == null || tibAddr.isZero()) {
    	VM.sysWrite(" (TIB is NULL)\n");
        return;
    }
    ObjectModel.dumpHeader(ref);
    ObjectReference tib = ObjectReference.fromObject(ObjectModel.getTIB(ref));
    if(tib.isNull()) {
    	VM.sysWrite(" (INVALID TIB: null)\n");
    	return;
    }
    if (!mappedVMRef(tib)) {
        VM.sysWrite(" (TIB OUTSIDE OF HEAP OR NOT MAPPED)\n");
        return;
    }
    if(!Space.isMappedObject(tib)) {
    	VM.sysWrite(" (INVALID TIB: not mapped)");
    	return;
    }
    if(!Space.isImmortal(tib)) {
    	VM.sysWrite(" (INVALID TIB: not immortal)");
    }
    if(!Space.isMappedObject(ObjectReference.fromObject(ObjectModel.getTIB(tib)))){
    	VM.sysWrite(" (INVALID TIB: last check failed)");
    }
    
    RVMType type = Magic.getObjectType(ref.toObject());
    ObjectReference itype = ObjectReference.fromObject(type);
    VM.sysWrite(" TYPE=");
    VM.sysWrite(itype);
    if (!validType(itype)) {
      VM.sysWrite(" (INVALID TYPE: CLASS NOT ACCESSIBLE)\n");
      return;
    }
    VM.sysWrite(" CLASS=");
    VM.sysWrite(type.getDescriptor());
    VM.sysWrite("\n");
  }

  @Uninterruptible
  public static void dumpRefSave(ObjectReference ref) {
    VM.sysWrite("REF=");
    if (ref.isNull()) {
      VM.sysWrite("NULL\n");
      return;
    }
    VM.sysWrite(ref);
    if (!mappedVMRef(ref)) {
      VM.sysWrite(" (REF OUTSIDE OF HEAP OR NOT MAPPED)\n");
      return;
    }
    ObjectModel.dumpHeader(ref);
    ObjectReference tib = ObjectReference.fromObject(ObjectModel.getTIB(ref));
    if (!MemoryManager.mightBeTIB(tib)) {
      VM.sysWrite(" (INVALID TIB: CLASS NOT ACCESSIBLE)\n");
      return;
    }
    RVMType type = Magic.getObjectType(ref.toObject());
    ObjectReference itype = ObjectReference.fromObject(type);
    VM.sysWrite(" TYPE=");
    VM.sysWrite(itype);
    if (!validType(itype)) {
      VM.sysWrite(" (INVALID TYPE: CLASS NOT ACCESSIBLE)\n");
      return;
    }
    VM.sysWrite(" CLASS=");
    VM.sysWrite(type.getDescriptor());
    VM.sysWrite("\n");
  }

  public static boolean addrInBootImage(Address addr) {
    return (addr.GE(BOOT_IMAGE_DATA_START) && addr.LT(BOOT_IMAGE_DATA_END)) ||
           (addr.GE(BOOT_IMAGE_CODE_START) && addr.LT(BOOT_IMAGE_CODE_END));
  }
  
  @Uninterruptible
  public static boolean validRefS(ObjectReference ref) {

    if (ref.isNull()) return true;
    if (!Space.isMappedObject(ref)) {
      VM.sysWrite("validRef: REF outside heap, ref = ");
      VM.sysWrite(ref);
      VM.sysWrite("\n");
      Space.printVMMap();
      return false;
    }
    if (MemoryManagerConstants.MOVES_OBJECTS) {
      /*
      TODO: Work out how to check if forwarded
      if (Plan.isForwardedOrBeingForwarded(ref)) {
        // TODO: actually follow forwarding pointer
        // (need to bound recursion when things are broken!!)
        return true;
      }
      */
    }

    TIB tib = ObjectModel.getTIB(ref);
    Address tibAddr = Magic.objectAsAddress(tib);
    if (!Space.isMappedObject(ObjectReference.fromObject(tib))) {
      VM.sysWrite("validRef: TIB outside heap, ref = ");
      VM.sysWrite(ref);
      VM.sysWrite(" tib = ");
      VM.sysWrite(tibAddr);
      VM.sysWrite("\n");
      //ObjectModel.dumpHeader(ref);
      return false;
    }
    if (tibAddr.isZero()) {
      VM.sysWrite("validRef: TIB is Zero! ");
      VM.sysWrite(ref);
      VM.sysWrite("\n");
      ObjectModel.dumpHeader(ref);
      return false;
    }
    if (tib.length() == 0) {
      VM.sysWrite("validRef: TIB length zero, ref = ");
      VM.sysWrite(ref);
      VM.sysWrite(" tib = ");
      VM.sysWrite(tibAddr);
      VM.sysWrite("\n");
      ObjectModel.dumpHeader(ref);
      return false;
    }

    ObjectReference type = ObjectReference.fromObject(tib.getType());
    if (!validType(type)) {
      VM.sysWrite("validRef: invalid TYPE, ref = ");
      VM.sysWrite(ref);
      VM.sysWrite(" tib = ");
      VM.sysWrite(Magic.objectAsAddress(tib));
      VM.sysWrite(" type = ");
      VM.sysWrite(type);
      VM.sysWrite("\n");
      ObjectModel.dumpHeader(ref);
      return false;
    }
    return true;
  }  // validRef
  
  // 11.02.09 5:01
  @Uninterruptible
	public static boolean validRefS1(ObjectReference ref) {

		if (ref.isNull())
			return true;
		if (!Space.isMappedObject(ref)) {
			if (Options.verbose.getValue() >= 9) {
				VM.sysWrite("validRef: REF outside heap, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite("\n");
			}
			return false;
		}
		if (MemoryManagerConstants.MOVES_OBJECTS) {
			/*
			 * TODO: Work out how to check if forwarded if
			 * (Plan.isForwardedOrBeingForwarded(ref)) { // TODO: actually
			 * follow forwarding pointer // (need to bound recursion when things
			 * are broken!!) return true; }
			 */
		}

		TIB tib = ObjectModel.getTIB(ref);

		// kathy - start
		ObjectReference tibOR = ObjectReference.fromObject(tib);
		if (tibOR.isNull()) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWriteln("validRef: (INVALID TIB: null)");
			}
			return false;
		}
		if (!Space.isMappedObject(tibOR)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWriteln("validRef: (INVALID TIB: not mapped)");
			}
			return false;
		}
		if (!Space.isImmortal(tibOR)) {
			// VM.sysWrite(" (INVALID TIB: not immortal)");
		}
		if (!Space.isMappedObject(ObjectReference.fromObject(ObjectModel
				.getTIB(tibOR)))) {
			// VM.sysWrite(" (INVALID TIB: last check failed)");
		}
		// kathy - end

		Address tibAddr = Magic.objectAsAddress(tib);
		if (!Space.isMappedObject(ObjectReference.fromObject(tib))) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: TIB outside heap, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}
		if (tibAddr.isZero()) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: TIB is Zero! ");
				VM.sysWrite(ref);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}
		if (tib.length() == 0) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: TIB length zero, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		RVMType rvmType = Magic.getObjectType(ref.toObject());
		ObjectReference itype = ObjectReference.fromObject(rvmType);
		// VM.sysWrite(" TYPE=");
		// VM.sysWrite(itype);
		if (!validType(itype)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite(" (INVALID TYPE: CLASS NOT ACCESSIBLE)\n");
			}
			return false;
		}

		ObjectReference type = ObjectReference.fromObject(tib.getType());
		if (!validType(type)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: invalid TYPE, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(Magic.objectAsAddress(tib));
				VM.sysWrite(" type = ");
				VM.sysWrite(type);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		return true;
	} // validRef

  @Uninterruptible
	public static boolean validRef(ObjectReference ref) {
		if (ref.isNull())
			return true;

		if (!mappedVMRef(ref)) {
			if (Options.verbose.getValue() >= 9) { 
				VM.sysWrite("validRef: INVALID REF OUTSIDE OF HEAP OR NOT MAPPED, ref = ");
				VM.sysWriteln(ref); 
			}
			return false;
		}
		
		if (!Space.isMappedObject(ref)) { 
			if (Options.verbose.getValue() >= 9) { 
				VM.sysWrite("validRef: INVALID REF outside heap, ref = ");
				VM.sysWriteln(ref); 
			} return false; 
		}
		 
		long longO = ref.toAddress().toLong();
		if (longO % 4 != 0) {
			if (Options.verbose.getValue() >= 9) { 
				VM.sysWrite("validRef: INVALID ref not alligned, ref=");
				VM.sysWriteln(ref); 
			}
			return false;
		}

		TIB tib = ObjectModel.getTIB(ref);
		// kathy - start
		ObjectReference tibOR = ObjectReference.fromObject(tib);
		if (tibOR.isNull()) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TIB is null, ref=");
				VM.sysWriteln(ref); 
			}
			return false;
		}
		if (!mappedVMRef(tibOR)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: TIB OUTSIDE OF HEAP OR NOT MAPPED, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", tibOR=");
				VM.sysWriteln(tibOR);
			}
			return false;
		}
		if (!Space.isMappedObject(tibOR)) { 
			if (Options.verbose.getValue() >=  5) { 
				VM.sysWrite("validRef: TIB NOT MAPPED, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", tibOR=");
				VM.sysWriteln(tibOR);
			} 
			return false; 
		}

		longO = tibOR.toAddress().toLong();
		if (longO % 4 != 0) {
			if (Options.verbose.getValue() >=  5) { 
				VM.sysWrite("validRef: tib address is not alligned ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", tibOR=");
				VM.sysWriteln(tibOR);
			}
			return false;
		}

		Address refStatus = Magic.getWordAtOffset(ref, JavaHeader.STATUS_OFFSET).toAddress();
		if (mappedVMRef(refStatus.toObjectReference())) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: status looks like heap object, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", status=");
				VM.sysWriteln(refStatus); 
			}
			return false;
		}
		
		/*
		Address tibStatus = Magic.getWordAtOffset(tibOR, JavaHeader.STATUS_OFFSET).toAddress();
		if (mappedVMRef(tibStatus.toObjectReference())) {
			if (Options.verbose.getValue() >= 1) {
				VM.sysWrite("validRef: tib status looks like heap object, ref=");
				VM.sysWrite(ref); 
				VM.sysWrite(", tibOR=");
				VM.sysWrite(tibOR);
				VM.sysWrite(", tibStatus=");
				VM.sysWriteln(tibStatus); 
			}
			return false;
		}*/
		
		if(!Space.isMappedObject(ObjectReference.fromObject(ObjectModel.getTIB(tibOR)))) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TIB gettib is not mapped, ref=");
				VM.sysWriteln(ref);
			}
			return false;
		}
		// kathy - end

		Address tibAddr = Magic.objectAsAddress(tib);
		if (!Space.isMappedObject(ObjectReference.fromObject(tib))) {
			if (Options.verbose.getValue() >= 1) {
				VM.sysWrite("validRef: INVALID TIB outside heap, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}
		if (tibAddr.isZero()) {
			if (Options.verbose.getValue() >= 1) {
				VM.sysWrite("validRef: INVALID TIB is Zero, ref=");
				VM.sysWrite(ref);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}
		longO = tibAddr.toLong();
		if (longO % 4 != 0) {
			if (DEBUG) {
				Log.write("validRef: tobAddr nor alligned ,ref=");
				Log.write(ref);
				Log.write(", tibAddr=");
				Log.writeln(tibAddr);
				Log.flush();
			}
			return false;
		}
		if (tib == null || tib.length() == 0) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TIB length zero, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(tibAddr);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		RVMType rvmType = Magic.getObjectType(ref.toObject());
		ObjectReference itype = ObjectReference.fromObject(rvmType);
		if (!mappedVMRef(itype)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: type OUTSIDE OF HEAP OR NOT MAPPED, ref=");
				VM.sysWrite(ref);
				VM.sysWrite(", itype=");
				VM.sysWriteln(itype);
			}
			return false;
		}
		
		longO = itype.toAddress().toLong();
		if (longO % 4 != 0) {
			// if(DEBUG) {
			// Log.write("!!! DebugUtil.validRef  called for ref=");
			// Log.write(ref);
			// Log.write(", itype=");
			// Log.writeln(itype);
			// Log.flush();
			// }
			return false;
		}

		//dumpRef(ref);
		
		if (!validType(itype)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: INVALID TYPE: CLASS NOT ACCESSIBLE, ref=");
				VM.sysWriteln(ref);
			}
			return false;
		}

		ObjectReference type = ObjectReference.fromObject(tib.getType());
		if (!validType(type)) {
			if (Options.verbose.getValue() >= 5) {
				VM.sysWrite("validRef: invalid TYPE, ref = ");
				VM.sysWrite(ref);
				VM.sysWrite(" tib = ");
				VM.sysWrite(Magic.objectAsAddress(tib));
				VM.sysWrite(" type = ");
				VM.sysWrite(type);
				VM.sysWrite("\n");
				ObjectModel.dumpHeader(ref);
			}
			return false;
		}

		return true;
	} // validRef
}
